﻿using System;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using Perspective.Wpf3D.Primitives;
using Perspective.Core.Primitives;
using Perspective.Core.Wpf;
using System.Windows;

namespace Perspective.Wpf3D.Sculptors
{
    /// <summary>
    /// 组成椭圆的所有点和三角形集合
    /// A class to handle points and triangles of a 3D ellipse model.
    /// Default radius is 10.0.
    /// </summary>
    public class EllipseSculptor : Sculptor
    {
        #region Constant
        internal const double DefaultParameterA = 2.0;
        internal const double DefaultParameterB = 1.0;
        #endregion

        #region Members
        
        #endregion

        #region Properties
        private Point3D _center;
        /// <summary>
        /// 中心点
        /// The ellipse's center point.
        /// </summary>
        public Point3D Center
        {
            get { return _center; }
            set { _center = value; }
        }

        private bool _centered;
        /// <summary>
        /// 是否有中心点
        /// Indicates if the sculptor has a center point.
        /// </summary>
        protected bool Centered
        {
            get { return _centered; }
            set { _centered = value; }
        }

       private double _parameterA=DefaultParameterA;
        /// <summary>
        /// 横轴半径
        /// Gets or sets the value of the horizontal radius.
        /// </summary>
        public double ParameterA
        {
            get { return _parameterA; }
            set { _parameterA = value; }
        }

        private double _parameterB=DefaultParameterB;
        /// <summary>
        /// 竖轴半径
        /// Gets or sets the value of the vertical radius.
        /// </summary>
        public double ParameterB
        {
            get { return _parameterB; }
            set { _parameterB = value; }
        }

        private int _circumferenceSideCount;
        /// <summary>
        /// 圆周的面数
        /// Gets or sets the side count of the circumference.
        /// </summary>
        protected int CircumferenceSideCount
        {
            get { return _circumferenceSideCount; }
            set { _circumferenceSideCount = value; }
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of PolygonSculptor.
        /// </summary>
        public EllipseSculptor()
            : base()
        {
        }

         /// <summary>
        /// Initializes a new instance of PolygonSculptor.
        /// Vertices are automatically generated
        /// </summary>
        /// <param name="circumferenceSideCount">Side count</param>
        /// <param name="parameterA">the value of the horizontal radius.</param>
        /// <param name="parameterB">the value of the vertical radius.</param>
        public EllipseSculptor(int circumferenceSideCount, double parameterA, double parameterB)
        {
            Initialize(circumferenceSideCount,parameterA,parameterB);
        }

        /// <summary>
        /// Initializes an existing instance of EllipseSculptor.
        /// </summary>
        /// <param name="circumferenceSideCount">Side count</param>
        /// <param name="parameterA">the value of the horizontal radius.</param>
        /// <param name="parameterB">the value of the vertical radius.</param>
        public void Initialize(int circumferenceSideCount, double parameterA, double parameterB)
        {
            _circumferenceSideCount = circumferenceSideCount;
            _parameterA = parameterA;
            _parameterB = parameterB;
            CreatePoints();
        }
        #endregion

        #region Functions
        /// <summary>
        /// Initializes the Points and Triangles collections.
        /// Called By Sculptor.BuildMesh()
        /// </summary>
        protected override void CreateTriangles()
        {
            if (Points.Count == 0)
            {
                // CreatePoints(_circumferenceSideCount);
                CreatePoints();
            }
            if ((Triangles.Count == 0) &&
                (Points.Count > 0))
            {
                BuildTriangles();
            }
        }

        /// <summary>
        /// Creates the points.
        /// Points are added by vertical rank from top to bottom (on Z-Y first)
        /// and then counter-clockwise.
        /// </summary>
        public void CreatePoints()
        {
            if (_circumferenceSideCount < 4)
            {
                throw new ArgumentException("parallelCount < 4");
            }
            _center = new Point3D(0, 0, 0);
            _centered = true;
            double angle1 = 2 * Math.PI / _circumferenceSideCount;
            double angle = 0.0;
            for (int i = 1; i <= _circumferenceSideCount; i++)
            {
                // angle = (angle1 * i);
                angle = (angle1 * i);// +GeometryHelper.DegreeToRadian(_initialAngle);
                Point3D p = new Point3D();
                p.X = Math.Cos(angle) * _parameterA;
                p.Y = Math.Sin(angle) * _parameterB;
                p.Z = 0;
                this.Points.Add(p);
            }
        }

        /// <summary>
        /// Build the triangles.
        /// </summary>
        public void BuildTriangles()
        {
            BuildTriangles(TriangleSideKind.Front);
        }

        /// <summary>
        /// Build the triangles, with a specific side orientation.
        /// </summary>
        /// <param name="tsk">TriangleSideKind value (triangle orientation).</param>
        public virtual void BuildTriangles(TriangleSideKind tsk)
        {
            this.Triangles.Clear();
            if (Points.Count < 3)
            {
                throw new ArgumentException("Points.Count < 3");
            }
            if (!_centered)
            {
                CreateSideTriangles(Points, tsk);
            }
            else
            {
                // center based triangles
                for (int i = 0; i <= Points.Count - 1; i++)
                {
                    if ((tsk & TriangleSideKind.Front) == TriangleSideKind.Front)
                    {
                        if (i < Points.Count - 1)
                        {
                            this.Triangles.Add(new Point3DTriplet(_center, Points[i], Points[i + 1]));
                        }
                        else
                        {
                            this.Triangles.Add(new Point3DTriplet(_center, Points[i], Points[0]));
                        }
                    }
                    if ((tsk & TriangleSideKind.Back) == TriangleSideKind.Back)
                    {
                        if (i < Points.Count - 1)
                        {
                            this.Triangles.Add(new Point3DTriplet(_center, Points[i + 1], Points[i]));
                        }
                        else
                        {
                            this.Triangles.Add(new Point3DTriplet(_center, Points[0], Points[i]));
                        }
                    }
                }
            }
        }

        /// <summary>
        /// A method for building the TextureCoordinates collection of the mesh.
        /// </summary>
        internal override void MapTexture()
        {
            foreach (Point3DTriplet pt in this.Triangles)
            {
                foreach (Point3D p in pt.Points)
                {
                    Mesh.TextureCoordinates.Add(
                        new Point(p.X, -p.Y));
                }
            }
        }
        #endregion
    }
}
